/*********************************
 *rpc_router模块
 *一个枚举四个类
A.服务描述类：1.业务回调函数2.参数信息描述3.返回值类型描述4.参数校验接口
B.服务管理类：管理服务--->hash_map<string,服务描述>
C.rpc_router类:1.服务注册接口，提供给dispatch模块
D.获取服务工厂类

 *********************************/
#pragma once

#include "../comm/net.hpp"
#include "../comm/message.hpp"

using namespace Message;
using namespace MuduoNet;
using namespace std;

namespace Server
{
    // 类型
    enum class VType
    {
        BOOL = 0,
        INTEGRAL,
        NUMERIC,
        STRING,
        ARRAY,
        OBJECT
    };
    class ServiceDescribe
    {
    public:
        using ptr = shared_ptr<ServiceDescribe>;
        using ServiceCB = function<void(const Json::Value &,Json::Value &)>;
        using ParamsDescribe = pair<string, VType>;
       ServiceDescribe(std::string &&mname, std::vector<ParamsDescribe> &&desc, 
                    VType vtype, ServiceCB &&handler) : 
                    _method_name(std::move(mname)),_cb(std::move(handler)), 
                    _params_desc(std::move(desc)), _return_type(vtype)
                {}
        bool ParamsCheck(const Json::Value &params)
        {
            // 对Params进行参数校验---判断描述的是否是参数字段是否存在，类型是否一致
            for (auto &desc : _params_desc)
            {
                if (params.isMember(desc.first) == false)
                {
                    ELog("参数字段完整性校验失败！%s 字段缺失！", desc.first.c_str());
                    return false;
                }
                if (Check(desc.second, params[desc.first]) == false)
                {
                    ELog("%s 参数校验失败！", desc.first.c_str());
                    return false;
                }
            }
            return true;
        }
        const string& Methodname()
        {
            return _method_name;
        }
        bool Call(const Json::Value &params,Json::Value &result)
        {
            _cb(params,result);
            if(RtypeCheck(result)==false)
            {
                ELog("回调处理函数中的响应信息校验失败！");
                        return false;
            }
            return true;
        }
    private:
        bool RtypeCheck(const Json::Value &val)
        {
            return Check(_return_type, val);
        }
        bool Check(VType vtype, const Json::Value &val)
        {
            switch (vtype)
            {
            case VType::BOOL:  return val.isBool();
            case VType::INTEGRAL:  return val.isIntegral();
            case VType::NUMERIC:  return val.isNumeric();
            case VType::STRING:  return val.isString();
            case VType::ARRAY:  return val.isArray();
            case VType::OBJECT: return val.isObject();
            }
            return false;
        }

    private:
        string _method_name;                 // 方法名称
        ServiceCB _cb;                       // 实际的业务回调函数
        vector<ParamsDescribe> _params_desc; // 参数字段描述
        VType _return_type;                  // 结果的作为参数返回类型
    };
    class SDescribeFactory
    {
    public:
        void setMethodName(const std::string &name)
        {
            _method_name = name;
        }
        void setReturnType(VType vtype)
        {
            _return_type = vtype;
        }
        void setParamsDesc(const std::string &pname, VType vtype)
        {
            _params_desc.push_back(ServiceDescribe::ParamsDescribe(pname, vtype));
        }
        void setCallback(const ServiceDescribe::ServiceCB &cb)
        {
            _cb = cb;
        }
        ServiceDescribe::ptr build()
        {
            return std::make_shared<ServiceDescribe>(std::move(_method_name),
                                                   std::move(_params_desc), _return_type, std::move(_cb));
        }

    private:
        string _method_name;
        ServiceDescribe::ServiceCB _cb;
        vector<ServiceDescribe::ParamsDescribe> _params_desc;
        VType _return_type;
    };
    class ServiceManger // 管理服务
    {
    public:
        using ptr = shared_ptr<ServiceManger>;
        void Insert(const ServiceDescribe::ptr &desc)
        {
            unique_lock<std::mutex> lock(_mutex);
            _services.insert(make_pair(desc->Methodname(),desc));
        }
        ServiceDescribe::ptr Select(const string &method_name)
        {
            unique_lock<std::mutex> lock(_mutex);
            auto it=_services.find(method_name);
            if(it==_services.end())
            {   
                return  ServiceDescribe::ptr();
            }
            return it->second;
        }
        void Remove(const string &method_name)
        {
             unique_lock<std::mutex> lock(_mutex);
           _services.erase(method_name);
        }

    private:
        mutex _mutex;
        unordered_map<string, ServiceDescribe::ptr> _services;
    };

    class RpcRouter
    {
    public:
        using ptr = shared_ptr<RpcRouter>;
        // 对rpc进行回调函数的的业务处理函数
        RpcRouter():_service_manger(make_shared<ServiceManger>())
        {}
        void OnRpcRequest(const BaseConnection::ptr &conn, RpcRequest::ptr &request)
        {
            // 1. 查询客户端请求的方法描述--判断当前服务端能否提供对应的服务
            auto service=_service_manger->Select(request->Method());
            if(service.get()==nullptr)
            {
                ELog("%s 服务未找到!",request->Method().c_str());
                return response(conn,request,Json::Value(),RCode::RCODE_NOT_FOUND_SERVICE);
            }
            DLog("服务已找到");
            // 2. 进行参数校验，确定能否提供服务
            if(service->ParamsCheck(request->Params())==false)
            {
                 ELog("%s 服务参数校验失败!",request->Method().c_str());
                return response(conn,request,Json::Value(),RCode::RCODE_INVALID_PARAMS);
            }
            // 3. 调用业务回调接口进行业务处理
            Json::Value result;
            bool ret=service->Call(request->Params(),result);
            if(ret==false)
            {
                ELog("%s 服务内部错误!",request->Method().c_str());
                return response(conn,request,Json::Value(),RCode::RCODE_INTERNAL_ERROR);
            }
            ELog("设置响应结果：%d", result.asInt());
            // 4. 处理完毕得到结果，组织响应，向客户端发送
            return response(conn,request,result,RCode::RCODE_OK);
        }
        void RegisterMethod(const ServiceDescribe::ptr& service)
        {
            return _service_manger->Insert(service);
        }
        private:
        void response(const BaseConnection::ptr &conn,const RpcRequest::ptr&req,
            const Json::Value &rsp,const RCode rcode)
        {
           auto msg=MessageFactory::create<RpcResponse>();
           msg->SetId(req->GetRid());
           msg->SetMtype(MessType::Mtype::RSP_RPC);
           msg->SetRcode(rcode);
           msg->SetResult(rsp);
           conn->Send(msg);
        }
    private:
        ServiceManger::ptr _service_manger;
    };

}